﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using Complaint.Common;
using Complaint.Models.Dtos;
using Complaint.Models.Dtos.Enums;
using Complaint.Models.Services;
using Complaint.Repositories;

namespace Complaint.Models
{
    public class UserModel : BaseModel<UserRepository>, IModel<User>
    {
        public int MinRequiredPasswordLength = User.MinRequiredPasswordLength;

        #region IModel<User> Members

        public IList<User> Retrieve()
        {

            return Repository.FindAll().ToList();
        }

        public void Create(User dto)
        {

            if (FindByName(dto.Email).Count > 0)
                throw new ApplicationException("A user with this email address already exists.");
            if (FindByName(dto.DisplayName).Count > 0)
                throw new ApplicationException("A user with this display name already exists");

            Repository.Create(dto);
            SendVerificationEmail(dto);
        }

        public void Update(User dto)
        {

            Repository.Update(dto);
        }

        public void Delete(User dto)
        {

            Repository.Delete(dto);
        }

        public IList<User> FindByName(string name)
        {

            return Repository.FindByName(name).ToList();
        }

        public User FindById(int id)
        {

            return Repository.FindById(id);
        }

        #endregion

        public IList<User> Retrieve(LoadUser loadOption)
        {

            return Repository.FindAll(loadOption).ToList();
        }

        public User FindById(int id, LoadUser loadOption)
        {

            return Repository.FindById(id, loadOption);
        }

        public IList<User> FindByName(string name, LoadUser loadOption)
        {

            return Repository.FindByName(name, loadOption).ToList();
        }

        public IList<User> FindByEmail(string email, LoadUser loadOption)
        {
            return Repository.FindByEmail(email, loadOption).ToList();
        }

        public bool Validate(string username, string password)
        {
            // throw exception if no user found or more than 1 user is returned.
            try
            {
                User u = FindByEmail(username, LoadUser.PasswordQuestion).Single();
                if (!u.Status)
                {
                    throw new ApplicationException("Account needs activation.");
                }
                var authenticated = ValidatePassword(u, password);
                if (authenticated)
                {
                    u.LastLoginDate = DateTime.Now;
                    Update(u);
                }
                return authenticated;
            }
            catch (ArgumentNullException au)
            {
                throw new ApplicationException("Failed to find user.", au);
            }
            catch (InvalidOperationException io)
            {
                throw new ApplicationException("Username does not exist.", io);
            }
        }

        public bool ValidatePassword(User u, string password)
        {
            var hashedPasswordAndSalt = CryptoUtility.CreatePasswordHash(password, u.Salt);
            return u.Password.Equals(hashedPasswordAndSalt);
        }

        public bool ChangePassword(User u, string oldPassword, string newPassword)
        {
            if (ValidatePassword(u, oldPassword))
            {
                u.Password = CryptoUtility.CreatePasswordHash(newPassword, u.Salt);
                u.LastModifiedDate = DateTime.Now;
                Update(u);
                return true;
            }
            return false;
        }

        public void Create(User u, int size)
        {
            var salt = CryptoUtility.CreateSalt(size);
            u.Salt = salt;
            u.Password = CryptoUtility.CreatePasswordHash(u.Password, salt);
            Create(u);
        }

        private static void SendVerificationEmail(User u)
        {
            var subject = string.Format("Welcome to iComplain, {0}", u.FullName);
            var message = ComposeEmail(u);

            var sendMailThread = new Thread(() => new MailService().Send(
                                                      u.Email,
                                                      subject,
                                                      message
                                                      )) { IsBackground = true };

            sendMailThread.Start();
        }

        private static string ComposeEmail(User u)
        {
            var sb = new StringBuilder();
            sb.AppendLine("Thank you for registering with iComplain.info. We hope our service can be of help to you and other fellow consumers.\n");
            sb.AppendLine("Please click on below link to activate your account.\n");
            sb.AppendFormat("http://www.icomplain.info/Account/Activate/{0}?key={1}", u.UserID, HttpUtility.UrlEncode(u.Salt));
            sb.AppendLine();

            return sb.ToString();
        }
    }
}